[bits 32]
%define ERROR_CODE nop
%define ZERO push 0

extern idt_table

section .data
global intr_entry_table
intr_entry_table:

%macro VECTOR 2
section .text
intr%1entry:

    %2
    ;保存上下文
    push ds
    push es
    push fs
    push gs
    pushad

    ;如果是从片上进入的中断，除了往从片上发送 EOI 外，还要往主片上发送 EOI
    mov al, 0x20
    out 0xa0, al
    out 0x20, al

    push %1                 ;不管 idt_table中的目标程序是否需要参数,都一律压入中断向量号，调试时很方便
    call [idt_table + %1*4] ;调用 idt_table 中的 C 版本中断处理函数
    jmp intr_exit

section .data
    dd intr%1entry
%endmacro

section .text
global intr_exit
intr_exit:
    ;恢复上下文
    add esp, 4              ;跳过中断号,之前压入的push %1
    popad
    pop gs
    pop fs
    pop es
    pop ds
    add esp, 4              ;跳过error_code
    iretd

VECTOR 0x00, ZERO
VECTOR 0x01, ZERO
VECTOR 0x02, ZERO
VECTOR 0x03, ZERO
VECTOR 0x04, ZERO
VECTOR 0x05, ZERO
VECTOR 0x06, ZERO
VECTOR 0x07, ZERO
VECTOR 0x08, ERROR_CODE ;8
VECTOR 0x09, ZERO
VECTOR 0x0a, ZERO
VECTOR 0x0b, ZERO
VECTOR 0x0c, ZERO
VECTOR 0x0d, ZERO
VECTOR 0x0e, ZERO
VECTOR 0x0f, ZERO
VECTOR 0x10, ZERO       ;16
VECTOR 0x11, ERROR_CODE ;17
VECTOR 0x12, ZERO
VECTOR 0x13, ZERO
VECTOR 0x14, ZERO
VECTOR 0x15, ZERO
VECTOR 0x16, ZERO
VECTOR 0x17, ZERO
VECTOR 0x18, ZERO
VECTOR 0x19, ZERO
VECTOR 0x1a, ZERO
VECTOR 0x1b, ZERO
VECTOR 0x1c, ZERO
VECTOR 0x1d, ZERO
VECTOR 0x1e, ZERO
VECTOR 0x1f, ZERO
VECTOR 0x20, ZERO       ;时钟
VECTOR 0x21, ZERO       ;键盘
VECTOR 0x22, ZERO       ;级联
VECTOR 0x23, ZERO       ;串口2
VECTOR 0x24, ZERO       ;串口1
VECTOR 0x25, ZERO       ;并口2
VECTOR 0x26, ZERO       ;软盘
VECTOR 0x27, ZERO       ;并口1
VECTOR 0x28, ZERO       ;实时时钟
VECTOR 0x29, ZERO       ;重定向
VECTOR 0x2a, ZERO       ;保留
VECTOR 0x2b, ZERO       ;保留
VECTOR 0x2c, ZERO       ;ps/2 鼠标
VECTOR 0x2d, ZERO       ;fpu浮点单元异常
VECTOR 0x2e, ZERO       ;硬盘
VECTOR 0x2f, ZERO       ;保留

;;;;;;;;;;;;;;;;;;;;;;;;;;;; 0x80 中断 ;;;;;;;;;;;;;;;;;;;;;;;;;
[bits 32]
extern syscall_table
section .text
global syscall_handler
syscall_handler:
    ;1 保存上下文
    push 0  ; 压入0 栈格式统一

    push ds
    push es
    push fs
    push gs
    pushad
    ;eax,ecx,edx,ebx,esp,ebp,esi,edi

    push 0x80   ;中断号,格式统一

    ;2 为系统调用子功能传入参数
    push edx    ;arg3
    push ecx    ;arg2
    push ebx    ;arg1

    ;3 使用子功能处理函数
    call [syscall_table + eax * 4]
    add esp, 12     ; 跨过上面三个参数

    ;4 将call 调用后的返回值存入当前内核栈中 eax 的位置
    mov [esp + 8*4], eax    ; 中断处理函数的返回值保存在eax中,将其存入pushad中入栈的eax位置,等待用户程序恢复上下文时,pop出eax作为返回值让用户程序能够接收到
    jmp intr_exit